package com.itheima.reggie.controller;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.itheima.reggie.common.R;
import com.itheima.reggie.dto.DishDto;
import com.itheima.reggie.entity.Category;
import com.itheima.reggie.entity.Dish;
import com.itheima.reggie.service.DishService;
import org.apache.commons.lang.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/dish")
public class DishController {

    @Autowired
    private DishService dishService;

    @Resource
    private RedisTemplate<String,Object> redisTemplate;


    @GetMapping("/page")
    public R<IPage<Dish>> sleectPage( Integer page,  Integer pageSize,String name){
        IPage<Dish> dishIPage = dishService.sleectPage(page, pageSize,name);
        return R.success(dishIPage);
    }
    /**
     * 批量删除
     */
    @DeleteMapping()
    public R<Boolean> deleteList(@RequestParam List<Long>  ids){
       boolean flage =  dishService.deleteList(ids);

       if (flage==false){
           return R.error("删除失败位置异常");
       }
        return R.success(flage);
    }

    /**
     * 批量停售  起售
     */
    @PostMapping("/status/{status}")
    public R<Boolean> updateStatus(@PathVariable("status") Integer state , @RequestParam List<Long> ids){

        boolean b = dishService.updateStatusx(state, ids);

        return R.success(b);
    }
    /**
     * 新增菜品
     */
    @PostMapping
    public R<Boolean> addDish(@RequestBody DishDto dish){

        boolean b = dishService.addDish(dish);

        return R.success(b);
    }
    /**
     * 查询菜品集合
     */
    @GetMapping("/list")
    public R<List<Dish>> selectList(Long categoryId,Integer status){
        List<Dish> dishes = dishService.selectList(categoryId,status);
        return R.success(dishes);
    }

    /**
     * 模拟缓存穿透的解决办法
     * 缓存击穿说的是：我们之前Redis的逻辑是：
     * 当用户访问我的服务的时候，第一次查询再Redis当中去查询
     * 如果当Redis当中没有此条数据，那么他就会去查询mysql当中的数据
     * 然后将此条数据存入到Redis缓存当中。
     * ------------------------------------------------------------
     * 那么这里的缓存击穿说的是，一个人拿着根本不存在的id来访问的我的服务器
     * 那么以上的逻辑根本应付不了这个问题，为什么呢？我们把mysql查出来的数据
     * 放到缓存当中，那么它拿了一个根本不存在的ID那么我的mysql根本就查不出来也就是null
     * 那么也就放入不了缓存当中（Redis命中率降低），那么如果大量的请求拿了根本不存在的ID来访问我的
     * 服务器，那么我的服务器根本就受不了压力（MySQL压力过大）
     *
     * 一个一定不存在的数据，由于缓存是不命中时被动写的，并且出于容错考虑，
     * 如果从存储层查不到数据则不写入缓存，这将导致这个不存在的数据每次请求都要到存储层去查询，
     * 失去了缓存的意义
     *
     *
     * 对空值缓存： 如果一个查询返回的数据为空（不管是数据是否不存在），我们仍然把这个空结果（null）进行缓存，
     * 这样可以缓解数据库的访问压力，然后设置空结果的过期时间会很短，最长不超过五分钟。（只能作为简单的应急方案）
     * 设置可访问的名单（白名单）： 使用 bitmaps 类型定义一个可以访问的名单，名单 id 作为 bitmaps 的偏移量，每次访问和 bitmap 里面的 id 进行比较，
     * 如果访问 id 不在 bitmaps 里面，进行拦截，不允许访问。
     * 布隆过滤器： 将所有可能存在的数据哈希到一个足够大的 bitmaps 中，一个一定不存在的数据会被这个bitmaps 拦截掉，从而避免了对底层存储系统的查询压力
     * 进行实时监控： 当发现 Redis 的命中率开始急速降低，需要排查访问对象和访问的数据，和运维人员配合，可以设置黑名单限制服务
     */
    //对空值进行缓存
    @GetMapping("/categoryId")
    public R<Dish> getID(@RequestParam Long categoryId){
         Dish id1 = dishService.getID(categoryId);

        return R.success(id1);
    }


    /**
     * 缓存击穿（有数据的）
     *
     * 缓存击穿其实说的是：大量合理的请求打到数据库（mysql）
     * 为什么会造成这样的情况，就是说我们其中一个热点数据（热点key）过期了
     * 那么在这个时候会存在一个问题，就是大量的并发访问突然打到我的数据库导致数据库怠机但是说这些数据是存在的
     * 也就是说是合理数据
     *
     * 现象：数据库的访问压力瞬间增大
     * Redis正常运行
     * Redis没有出现大量的过期现象（过期后无法访问，如果命中，则无需访问数据库）
     *
     * 产生原因： redis中的某个热门的key过期了，而此时客户端对这个key的访问量激增，
     * redis无法命中，这些访问就会转发到数据库，造成数据库瞬间压力过大
     *
     * 解决方案
     * key 可能会在某些时间点被超高并发地访问，是一种非常“热点”的数据。这个时候，需要考虑一个问题：缓存被“击穿”的问题。
     *
     * 预先设置热门数据： 在 redis 高峰访问之前，把一些热门数据提前存入到redis 里面，加大这些热门数据 key 的时长
     * 实时调整： 现场监控哪些数据热门，实时调整 key 的过期时长
     * 使用锁：（1）就是在缓存失效的时候（判断拿出来的值为空），不是立即去 load DB；
     * （2） 先使用缓存工具的某些带成功操作返回值的操作（比如 Redis 的 SETNX）去 set 一个 mutex key；
     * （3） 当操作返回成功时，再进行 load db 的操作，并回设缓存,最后删除 mutex key；
     * （4） 当操作返回失败，证明有线程在 load db，当前线程睡眠一段时间再重试整个 get 缓存的方法
     *
     *
     * 互斥锁：
     * 实现逻辑是这样：
     * 当其中的一个热点数据过期了吗，
     * 那么在这个时候我去上锁，那么这个线程就从并行变成了串行
     * 在这个时候，第一个人去到了缓存当中，判断缓存中是否存在数据，
     * 那么没有存在数据，那么他会去尝试获取锁，直接去访问MySQL数据库
     * 根据ID查询出数据，将信息写回Redis当中然后释放锁，那么第二个人就会一直等待锁的释放
     * 那么当锁释放之后那么这个人就可以直接从Redis当中获取数据
     */

    @GetMapping("/selectID")
    public R<Dish> selectID(@RequestParam Long selectID){
        Dish dish  = dishService.selectID(selectID);
        return R.success(dish);
    }



}
